//This code originally came from the Visual Studio SDK for VS2005 and has been customized.
//This code was obtained from the 2006.09 version of the SDK.
//The original version can be found under the name of: C# Example.SyncronousXMLDesigner
//***************************************************************************
//
//    Copyright (c) Microsoft Corporation. All rights reserved.
//    This code is licensed under the Visual Studio SDK license terms.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//***************************************************************************

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio;
using System.ComponentModel.Design;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.Security.Permissions;

using IOleCommandTarget = Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget;
using IServiceProvider = System.IServiceProvider;
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
using MSVSIP = Microsoft.VisualStudio.Shell;
using EventAbstractionAPI.RuleEditor;
using EventAbstractionAPI.RuleTableCtrl;
using EnvDTE;
using EnvDTE80;
using System.Reflection;
using VSLangProj80;
using VSLangProj;
using System.Collections;


namespace Microsoft.Xml.Designers
{
    public sealed class R2MLEditor : UserControl,
                                    IVsWindowPane,
                                    IOleCommandTarget,
                                    IVsDeferredDocView,
                                    IVsTextLinesEvents
    {
        private ServiceProvider vsServiceProvider = null;
        private IVsTextLines buffer = null;

        private string fileName;
        private string lastxml;
        private SplitContainer splitContainer1;
        private TextBox textBox1;
        private EventAbstractionAPI.RuleTableCtrl.RuleTableCtrl ruleTableCtrl1;
        private RuleEditorPackage myPackage;
        private uint cookie;

        // Flag true when we receiving a OnTextChange event for a user edit
        private bool updatingOnUserEdit;

        // This flag is true when we are asking the QueryEditQuerySave service if we can edit the
        // file. It is used to avoid to have more than one request queued
        private bool gettingCheckoutStatus;
        

        public R2MLEditor()
        {
            //This is not used!!!!!!
        }

        public R2MLEditor(RuleEditorPackage package, string pszMkDocument, IVsTextLines textBuffer)
        {
            
            PrivateInit(package, pszMkDocument, textBuffer);

            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();

            ruleTableCtrl1.ViewUpdateOccured += new EventAbstractionAPI.RuleTableCtrl.RuleTableCtrl.UpdateHandler(DocumentContentChangedHandler);
        }

        ~R2MLEditor()
        {
            Dispose(false);
        }



        private void PrivateInit(RuleEditorPackage package, string pszMkDocument, IVsTextLines textBuffer)
        {
            
            myPackage = package;
            buffer = textBuffer;
            fileName = pszMkDocument;
        }

        public void InitializeComponent()
        {
            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
            this.ruleTableCtrl1 = new EventAbstractionAPI.RuleTableCtrl.RuleTableCtrl();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.splitContainer1.Panel1.SuspendLayout();
            this.splitContainer1.Panel2.SuspendLayout();
            this.splitContainer1.SuspendLayout();
            this.SuspendLayout();
            // 
            // splitContainer1
            // 
            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer1.Location = new System.Drawing.Point(0, 0);
            this.splitContainer1.Name = "splitContainer1";
            this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
            // 
            // splitContainer1.Panel1
            // 
            this.splitContainer1.Panel1.Controls.Add(this.ruleTableCtrl1);
            // 
            // splitContainer1.Panel2
            // 
            this.splitContainer1.Panel2.Controls.Add(this.textBox1);
            this.splitContainer1.Size = new System.Drawing.Size(642, 467);
            this.splitContainer1.SplitterDistance = 373;
            this.splitContainer1.TabIndex = 1;
            // 
            // ruleTableCtrl1
            // 
            this.ruleTableCtrl1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.ruleTableCtrl1.Location = new System.Drawing.Point(0, 0);
            this.ruleTableCtrl1.Name = "ruleTableCtrl1";
            this.ruleTableCtrl1.Size = new System.Drawing.Size(642, 373);
            this.ruleTableCtrl1.TabIndex = 0;
            this.ruleTableCtrl1.Load += new System.EventHandler(this.ruleTableCtrl1_Load);
            // 
            // textBox1
            // 
            this.textBox1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.textBox1.Location = new System.Drawing.Point(0, 0);
            this.textBox1.Multiline = true;
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(642, 90);
            this.textBox1.TabIndex = 0;
            // 
            // R2mlViewer
            // 
            this.Controls.Add(this.splitContainer1);
            this.Name = "R2MLViewer";
            this.Size = new System.Drawing.Size(642, 467);
            this.splitContainer1.Panel1.ResumeLayout(false);
            this.splitContainer1.Panel2.ResumeLayout(false);
            this.splitContainer1.Panel2.PerformLayout();
            this.splitContainer1.ResumeLayout(false);
            this.ResumeLayout(false);

            
        }

        #region IVsWindowPane Members

        public int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp)
        {
            vsServiceProvider = new ServiceProvider(psp);

            return VSConstants.S_OK;
        }

        int Microsoft.VisualStudio.Shell.Interop.IVsWindowPane.TranslateAccelerator(MSG[] lpmsg)
        {
            return VSConstants.S_FALSE;
        }

        int Microsoft.VisualStudio.Shell.Interop.IVsWindowPane.SaveViewState(IStream pStream)
        {
            return VSConstants.S_OK;
        }

        int Microsoft.VisualStudio.Shell.Interop.IVsWindowPane.LoadViewState(IStream pStream)
        {
            return VSConstants.S_OK;
        }

        int Microsoft.VisualStudio.Shell.Interop.IVsWindowPane.GetDefaultSize(SIZE[] size)
        {
            if (size.Length >= 1)
            {
                size[0].cx = Size.Width;
                size[0].cy = Size.Height;
            }

            return VSConstants.S_OK;
        }

        int Microsoft.VisualStudio.Shell.Interop.IVsWindowPane.CreatePaneWindow(System.IntPtr hwndParent, int x, int y, int cx, int cy, out System.IntPtr hwnd)
        {   //SyncData();
            int cLines;
            int cLineLen;
            string text;
            
            AdviseTextLinesEvents(true);

            buffer.GetLineCount(out cLines);
            buffer.GetLengthOfLine(cLines - 1, out cLineLen);
            buffer.GetLineText(0, 0, cLines - 1, cLineLen, out text);

            try
            {
                ruleTableCtrl1.updateUI(text);
            }
            catch (XmlException e)
            {
                throw;
                //ProcessXmlException(e);
            }
            
            

            Microsoft.VisualStudio.Win32Methods.SetParent(Handle, hwndParent);
            hwnd = Handle;

            Size = new System.Drawing.Size(cx - x, cy - y);
            return VSConstants.S_OK;
        }

        public int ClosePane()
        {
            this.Dispose(true);
            return VSConstants.S_OK;
        }

        #endregion

        #region IOleCommandTarget Overrides

        public int QueryStatus(ref Guid guidCmdGroup, uint cCmds, OLECMD[] prgCmds, System.IntPtr pCmdText)
        {
            if ((prgCmds == null))
                return VSConstants.E_INVALIDARG;

            OLECMDF cmdf = OLECMDF.OLECMDF_DEFHIDEONCTXTMENU | OLECMDF.OLECMDF_INVISIBLE | OLECMDF.OLECMDF_LATCHED;

            if (guidCmdGroup == VSConstants.GUID_VSStandardCommandSet97)
            {
                // Process standard Commands
                switch (prgCmds[0].cmdID)
                {
                    case (uint)14:  //View Code item on context menu
                        cmdf = OLECMDF.OLECMDF_SUPPORTED | OLECMDF.OLECMDF_ENABLED;
                        break;
                    default:
                        return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
                }

            }
            else
                return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED); ;

            prgCmds[0].cmdf = (uint)cmdf;

            return VSConstants.S_OK;
        }

        public int Exec(ref Guid guidCmdGroup, uint nCmdID, uint nCmdexecopt, System.IntPtr pvaIn, System.IntPtr pvaOut)
        {
            if (guidCmdGroup == VSConstants.GUID_VSStandardCommandSet97)
            {
                switch (nCmdID)
                {
                    case (uint)14:  //View Code item on context menu
                        EnvDTE.DTE dte = this.vsServiceProvider.GetService(typeof(EnvDTE._DTE)) as EnvDTE.DTE;
                        if (dte != null)
                        {
                            dte.ItemOperations.OpenFile(fileName, EnvDTE.Constants.vsViewKindTextView);
                        }
                        break;

                    default:
                        return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
                }
            }
            else
                return (int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_UNKNOWNGROUP;

            return VSConstants.S_OK;
        }
        #endregion

        #region IVsDeferredDocView Members

        public int get_CmdUIGuid(out Guid pGuidCmdId)
        {
            pGuidCmdId = GuidList.guidEditorFactory;
            return VSConstants.S_OK;
        }

        public int get_DocView(out IntPtr ppUnkDocView)
        {
            ppUnkDocView = Marshal.GetIUnknownForObject(this);
            return VSConstants.S_OK;
        }

        #endregion

        #region IVsTextLinesEvents Members
        /// <summary>
        /// This method is invoked when the text line attributes have been changed
        /// </summary>
        /// <param name="iFirstLine">First affected line, inclusive</param>
        /// <param name="iLastLine">Last affected line, inclusive</param>
        public void OnChangeLineAttributes(int iFirstLine, int iLastLine)
        {
            // don't care about line color changes in our custom editor view
        }

        /// <summary>
        /// This method is invoked when the content of a text line in the buffer has changed
        /// </summary>
        /// <param name="pTextLineChange">Pointer to the TextLineChange structure that defines the shape of the old and new text</param>
        /// <param name="fLast">Obsolete</param>
        public void OnChangeLineText(TextLineChange[] pTextLineChange, int fLast)
        {
            // We don't need to update the control if this is a change to 
            // the TextBuffer caused by our own user edit
            if (!ruleTableCtrl1.updatingOnUserEdit)
            {
                int cLines;
                int cLineLen;
                string text;
                buffer.GetLineCount(out cLines);
                buffer.GetLengthOfLine(cLines - 1, out cLineLen);
                buffer.GetLineText(0, 0, cLines - 1, cLineLen, out text);

                try
                {
                    ruleTableCtrl1.updateUI(text);
                }
                catch (XmlException e)
                {
                    throw;
                    //ProcessXmlException(e);
                }
            }
        }

        #endregion

        #region ViewEvents
        /// <summary>
        /// Sets text to the TextBuffer
        /// Taken from Microsoft SynchronousXMLDesigner example of VSSDK.
        /// </summary>
        /// <param name="newText">Text to set</param>
        private void SetText(string newText)
        {
            int endLine, endCol;
            ErrorHandler.ThrowOnFailure(buffer.GetLastLineIndex(out endLine, out endCol));
            int len = (newText == null) ? 0 : newText.Length;
            IntPtr pText = Marshal.StringToCoTaskMemAuto(newText);
            try
            {
                ErrorHandler.ThrowOnFailure(buffer.ReplaceLines(0, 0, endLine, endCol, pText, len, null));
            }
            finally
            {
                Marshal.FreeCoTaskMem(pText);
            }
        }

        /// <summary>
        /// Event handler
        /// Taken from Microsoft SynchronousXMLDesigner example of VSSDK.
        /// Modified to remove parameters
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DocumentContentChangedHandler()
        {
            // Check if the QueryEditQuerySave service allow us to change the file
            //if (!CanEditFile())
            //{
            //    // We can not change the file (e.g. a checkout operation failed), do exit
            //    return;
            //}
            try
            {
                updatingOnUserEdit = true;
                SetText(ruleTableCtrl1.getXML());
            }
            finally
            {
                updatingOnUserEdit = false;
            }
        }

        /// <summary>
        /// This function asks to the QueryEditQuerySave service if it is possible to
        /// edit the file
        /// Taken from Microsoft SynchronousXMLDesigner example of VSSDK.
        /// </summary>
        private bool CanEditFile()
        {
            // Check the status of the recursion guard
            if (gettingCheckoutStatus)
                return false;

            try
            {
                // Set the recursion guard
                gettingCheckoutStatus = true;

                // Get the QueryEditQuerySave service
                IVsQueryEditQuerySave2 queryEditQuerySave = (IVsQueryEditQuerySave2)GetService(typeof(SVsQueryEditQuerySave));

                // Now call the QueryEdit method to find the edit status of this file
                string[] documents = { this.fileName };
                uint result;
                uint outFlags;

                // Note that this function can popup a dialog to ask the user to checkout the file.
                // When this dialog is visible, it is possible to receive other request to change
                // the file and this is the reason for the recursion guard
                int hr = queryEditQuerySave.QueryEditFiles(
                    0,              // Flags
                    1,              // Number of elements in the array
                    documents,      // Files to edit
                    null,           // Input flags
                    null,           // Input array of VSQEQS_FILE_ATTRIBUTE_DATA
                    out result,     // result of the checkout
                    out outFlags    // Additional flags
                );
                if (ErrorHandler.Succeeded(hr) && (result == (uint)tagVSQueryEditResult.QER_EditOK))
                {
                    // In this case (and only in this case) we can return true from this function
                    return true;
                }
            }
            finally
            {
                gettingCheckoutStatus = false;
            }
            return false;
        }

        #endregion

        /// <summary>
        /// Helper routine to register our view within the visual studio
        /// </summary>
        /// <param name="subscribe">Subscribe or to Unsubscribe flag</param>
        private void AdviseTextLinesEvents(bool subscribe)
        {
            IConnectionPointContainer conPtCont;
            conPtCont = (IConnectionPointContainer)buffer;
            if (conPtCont != null)
            {
                IConnectionPoint conPt;
                Guid iidConPt = typeof(IVsTextLinesEvents).GUID;
                conPtCont.FindConnectionPoint(ref iidConPt, out conPt);
                if (subscribe)
                {
                    conPt.Advise((IVsTextLinesEvents)this, out cookie);
                }
                else
                {
                    // If the goal is to unsubscribe, but there is no subscription active, exit
                    if (cookie == VSConstants.VSCOOKIE_NIL)
                    {
                        return;
                    }
                    conPt.Unadvise(cookie);
                    cookie = VSConstants.VSCOOKIE_NIL;
                }
            }

            // register our independent view with the IVsTextManager so that it knows
            // the user is working with a view over the text buffer. this will trigger
            // the text buffer to prompt the user whether to reload the file if it is
            // edited outside of the development Environment
            IVsTextManager textManager = (IVsTextManager)GetService(typeof(SVsTextManager));

            if (textManager != null)
            {
                if (subscribe)
                {
                    textManager.RegisterIndependentView((IVsWindowPane)this, (IVsTextLines)buffer);
                }
                else
                {
                    textManager.UnregisterIndependentView((IVsWindowPane)this, (IVsTextLines)buffer);
                }
            }
        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        private void SyncData()
        {
            object service = this.vsServiceProvider.GetService(typeof(SVsUIShell));
            IVsUIShell shell = (IVsUIShell)service;

            IVsTextLines buffer = this.buffer;
            if (buffer != null)
            {

                int lines;
                int length;
                string text;
                try
                {
                    if (buffer.GetLineCount(out lines) != VSConstants.S_OK)
                        return;
                    if (buffer.GetLengthOfLine(lines - 1, out length) != VSConstants.S_OK)
                        return;
                    if (buffer.GetLineText(0, 0, lines - 1, length, out text) != VSConstants.S_OK)
                        return;

                    if (lastxml != text)
                    {
                        lastxml = text;
                        //The GUI update method is below
                        //BaseUri could be sent in as a parmeter
                        this.ruleTableCtrl1.updateUI(text);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.InnerException.Message);
                }
            }
        }
        
        public Uri BaseUri
        {
            get
            {
                string fname = null;
                try
                {
                    IVsUserData ud = this.buffer as IVsUserData;
                    if (ud != null)
                    {
                        object oname;
                        Guid GUID_VsBufferMoniker = typeof(IVsUserData).GUID;
                        ud.GetData(ref GUID_VsBufferMoniker, out oname);
                        if (oname != null) fname = oname.ToString();
                    }
                }
                catch (COMException)
                {
                }
                if (string.IsNullOrEmpty(fname))
                {
                    try
                    {
                        IPersistFileFormat fileFormat = this.buffer as IPersistFileFormat;
                        if (fileFormat != null)
                        {
                            uint format;
                            fileFormat.GetCurFile(out fname, out format);
                        }
                    }
                    catch (COMException)
                    {
                    }
                }
                if (!string.IsNullOrEmpty(fname))
                {
                    Uri baseUrl = new Uri(Application.StartupPath + System.IO.Path.DirectorySeparatorChar);
                    Uri relUrl = new Uri(fname);
                    return new Uri(baseUrl, relUrl);
                }
                return null;
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                AdviseTextLinesEvents(false);
                //dispose managed resources
                //Close();
            }
            //dispose unmanaged resources
        }

        private void ruleTableCtrl1_Load(object sender, EventArgs e)
        {

        }
    }
}


